#ifndef RENDERER
#define RENDERER

#include "GE_Base.h"
namespace Syp
{
///A structure describing hardware capabilities. Useful for enquiring current hardware capabilities or setting minimum hardware required for a Pass.
struct HardwareCapabilities
{
	///Support for VertexShaders.
    bool VertexShaders;
	///Support for PixelShaders.
	bool PixelShaders;
	///Number of TextureUnits available on hardware.
	PShort TextureUnits;

	//...etc
};
///The Rendering core.Renderer is a singleton as there should only ever be 1 instance of a Rendering system.
class SYPAPI Renderer
{
public:
	///Draws a frame
	void run();
	///Returns a reference to the Rendering system
	inline static Renderer& getRenderer()
	{
		assert(rnd && "Renderer cannot be returned until it has been initialised!");
		return (*rnd);
	};	
	inline int getWidth()
	{
        return Width;
	};

	inline int getHeight()
	{
        return Height;
	};
	inline RenderMode getCurrMode()
	{
		return currMode;
	};
	///Updates a mesh contents by sending it to gfx card, user has to manually call	updateMesh should he make any changes after addMesh().
	void updateMesh(BaseMesh* m);
	void updateIndices(IndexedMesh* m);
	///Removes a mesh/all instances as well. Deletes memory used by Mesh as well.
	void removeMesh(BaseMesh* m);

	///HighLevel draw function which does all setup and drawing of a instance.
	void draw(MeshInstance* m, PInt drawCall, Pass* p);	

	///Releases the Renderer
	static void release();	
	///Initialises Renderer
	static void initRenderer(int width, int height);		
	///Mesh specific drawing methods
	void drawIndexed(IndexedMesh* m);
	void drawNIndexed(NIndexedMesh* m);

	void clearColorDepthBuffer();
	void clearDepthBuffer();
	void clearColorBuffer();	

	///Sorting of instances based on sorting mode.
	//void doTextureSort();
	///Renderer decides best way to transform mesh after it matrices have been calculated. If VertexPrograms are supported, vertices are transformed on the graphics card. User should not call this method. NOTE: Could be ported to Pass.
	void AnimateBoneMesh(IndexedMesh* m);
	///Swaps buffers. Should be called at the end of everything by the Engine only. Users should call SypEngine::EndFrame()
	void swapBuffers();
	///You can define a rectangular portion of your window and restrict drawing to take place within it.
	void setScissorsRegion(int x, int y, int width, int height);

	///Generates a texture object in VRAM
	void generateTexObj(TextureObj* to);

	///Goes into Ortho mode
	void setUpOrtho();
	///Goes into Perspective mode
	void setUpPerspective();
	///Setsup scene cameras, this function is where the camera is actually setup
	void setUpCamera();
	///Sets the camera to be used for rendering the scene. Camera is setup again when this function is called.
	void setActiveCamera(Camera* cam);
	///Tells the renderer to bind a texture. Renderer double-checks if texture is already bound to avoid unnecessary expensive texture switch.
	void bindTexture(TextureObj* textureID);
	///Binds a Material.
	void bindMaterial(Material* mat);
	///Binds a SubMaterial.
	void bindSubMaterial(SubMaterial& mat);
	///Tells the renderer to delete this texture from VRAM
	void removeTexture(TextureObj* to);
	///Fills TextureObj with the image information contained in data. Format/size of data should be correctly contained in the TextureObj.
	void fillTexObj(TextureObj* to, unsigned char* data);
	///Does required pending VRAM submissions. Automatically called by SypEngine every frame.
	void doAdmin();

	///Does all the setting up of Lights based on numLights value.
	void setUpLights(PInt numLights);

	///Shader Stuff
	///Compiles a vertex shader.
	void compileVertexShader(VertexShader* vs);
	///Compiles a pixel shader.
	void compilePixelShader(PixelShader* ps);
	///Links all shaders in a ShaderProgram
	void linkShaders(ShaderProgram* sp);
	///Creates and returns a VertexShader handle
	PInt generateVertexShader();
	///Creates and returns a PixelShader handle
	PInt generatePixelShader();
	///Creates and returns a ShaderProgram handle
	PInt generateShaderProgram();
	///Attaches a Shader to a ShaderProgram
	void attachShaderToProgram(Shader* s,ShaderProgram* sp);	
	///Deletes a shader and removes it.
    void deleteShader(Shader* s);
	///Deletes a ShaderProgram
	void deleteShaderProgram(ShaderProgram* sp);
	///Returns the Mesh currently being drawn.Intended for use by Passes.
	BaseMesh* getCurrentMesh();
	///Returns the MeshInstance currently being drawn.Intended for use by Passes.
	MeshInstance* getCurrentMI();
	///Sets the global ambient value which will affect all objects in the scene including those not being lit by lights.
	void setGlobalAmbience(const Point4& globalAmb);
	///Sets up a light in the scene
	void setUpLight(Light* l);
	///Returns a const ref to the current HardWare's capabilities.
	const HardwareCapabilities& getHardwareCpbRef();
	///called when ever window size changes
	static void resizeWindow(int width,int height);
protected:
	///Friend functions are required to prevent or reduce the chances of the user doing something stupid with them.
	///For generating VBOs and keeping GL calls in Renderer class. User should not call    Renderer::addMesh, should only be called by MeshManager's friend functions. 
	///VBOs	are removed by their Mesh destructors which call removeFromVRAM().
	friend class NIndexedMesh;
	friend class IndexedMesh;

	friend void IndexedMesh::removeFromVRAM();
	friend void NIndexedMesh::removeFromVRAM();


	///Deletes VRAM used by mesh, should be called only by mesh's destructor
	void removeMeshFromVRAM(IndexedMesh* m);
	void removeMeshFromVRAM(NIndexedMesh* m);
	
	///NOT TO BE CALLED BY USER, all meshes are added on creation.
	void addMesh(IndexedMesh* m);
	///NOT TO BE CALLED BY USER, all meshes are added on creation.
	void addMesh(NIndexedMesh* m);
	
	///Calculates memory required by mesh for VBO
	PInt calcVBOsize(BaseMesh* m);	
	///Setups blending
	void setUpBlendType(BlendType b);

	///Pointer to the active camera
	Camera* activeCamera;


	


	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////	
	///ID of texture for fonts
	//PInt fontTexture;
	///Checks if a particular extension is supported
	bool checkExtension(std::string item);
	///Stores the extensions supported by user's gfx card
	String extensions;
	///returns the extensions supported by the Gfx card
	const char* Renderer::getExtensions();	
	///Sets up the Viewport for rendering
	void setUpViewport();
	///Sets up default OpenGL states and other initialisation
	void initGL();
	///Horizontal Resolution of screen
	int Width;
	///Vertical Resolution of screen
	int Height;		
private:
	///Universal Shader compiler(OpenGL). Different higher levels compiler functions for vertex/pixel shaders exist because not all apis might be able to universally compile like OpenGL.
	void compileShader(Shader* vs);
	SYPMutex TextureSubmissionMutex;
	///List of TextureSubmissions Renderer has to make
	std::list<TextureObj*> TextureSubmissionList;
	///The actual texture data for submission.
	std::list<unsigned char*> TextureDataSubmission;
	///Optimisation to prevent 8 bytes of unecessary transfer to IndexedMesh then back to Renderer.
	PInt currDrawCall;
	///Constructor of Renderer is private so that user cannot new it
	Renderer();
	///Destructor of Renderer is private so that user cannot delete it
	~Renderer();
	///The renderer Object
	static Renderer* rnd;
	///Currently bound texture
	TextureObj* boundTexture;
	///Currently bound Material
	Material* boundMaterial;

	////////////Lighting Stuff/////////////	
	PByte currActiveLights;	

	RenderMode currMode;
	BlendType currBlendMode;

	BaseMesh* currentMesh;
	MeshInstance* currentMI;

	///OpenGL Extensions definition

	HardwareCapabilities gfxcard;
	

	/// Sets up glVertexPointer/glNormalPointer/etc.. based on VertexType
	void setUpVertexPointers(BaseMesh* m);	
	inline void unSetVertexPointers();

	
};
}
#endif

